#Copyright (C) Nial Peters 2012
#
#This file is part of AvoScan.
#
#AvoScan is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#AvoScan is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with AvoScan.  If not, see <http://www.gnu.org/licenses/>.
"""
The startup module contains the GUI elements that are loaded before the
main AvoScan window is created - the connection dialog and the set zenith window.
"""

import wx
import threading
import time

from avoscan import scanner
from avoscan.gui import manual
from avoscan.gui.artwork import AvoscanArtProvider
from avoscan.gui.main import MainFrame


class WaitingForConnectionDialog(wx.Dialog):
    """
    Dialog that is displayed whilst AvoScan attempts to connect to a scanning
    unit. If no scanner is detected, then a warning message is issued and the 
    program exits.
    """
    def __init__(self, parent):
        self.cancelled = False
        
        self.__scanner = scanner.Scanner()
        self.__scanner.set_detach_handler(self.on_scanner_disconnect)
        
        # Call wxDialog's __init__ method
        wx.Dialog.__init__(self, parent, -1, "AvoScan")
        
        # Create a panel for our dialog
        self.panel = wx.Panel(self, -1)
        
        # Create a label
        self.label = wx.StaticText(self.panel, -1, "Searching for scanner...")
        
        self.vsizer = wx.BoxSizer(wx.VERTICAL)
        self.hsizer = wx.BoxSizer(wx.HORIZONTAL)
        self.hsizer.AddSpacer(20)
        art = AvoscanArtProvider()
        wx.ArtProvider.Push(art)
        icon = wx.StaticBitmap(self.panel, bitmap=art.GetBitmap("Avoscan", client=wx.ART_MESSAGE_BOX))
        self.SetIcon(art.GetIcon("Avoscan"))
        
        self.hsizer.Add(icon, 0, wx.ALIGN_LEFT) 
        self.hsizer.AddSpacer(5)
        self.hsizer.Add(self.label, 0, wx.ALIGN_CENTER)
        self.hsizer.AddSpacer(20)
        self.vsizer.AddSpacer(20)
        self.vsizer.Add(self.hsizer, 0, wx.ALIGN_CENTER)
        self.vsizer.AddSpacer(10)
        self.cancel_button = wx.Button(self.panel, wx.ID_ANY, "Cancel")
        self.vsizer.Add(self.cancel_button, 0 , wx.ALIGN_CENTRE_HORIZONTAL)
        self.vsizer.AddSpacer(20)
        
        # Attach the sizer to the panel
        self.panel.SetSizerAndFit(self.vsizer)
        
        # Resize the dialog
        self.vsizer.Fit(self)
        
        wx.EVT_BUTTON(self, self.cancel_button.GetId(), self.onCancel)
        wx.EVT_CLOSE(self, self.onCancel)
    
    
    def connectToScanner(self, timeout):
        """
        Attempt to connect to a scanner. Give up after 'timeout' seconds.
        Whilst the attempt is active, the dialog (i.e. self) will be displayed.
        """
        t = threading.Thread(target=self.__init_scanner, args=(timeout,))
        t.start()
        result = self.ShowModal()
        t.join()
        
        self.Show(False)
        
        wx.CallAfter(self.Destroy)
        if result == wx.ID_CANCEL:
            if not self.cancelled:
                wx.MessageBox("Error! Could not connect to scanner.", "AvoScan", wx.ICON_ERROR)
            return None

        return self.__scanner
        
    
    def __init_scanner(self, timeout):
        """
        Attempt to initialise the scanner. If timeout (in seconds) is reached
        then scanner.ScannerConnectionTimeoutError is raised.
        """
        while not self.IsModal():
            time.sleep(1)
        try:
            self.__scanner.initialise(timeout)
            if not self.cancelled:
                wx.CallAfter(self.EndModal, wx.ID_OK)
            else:
                wx.CallAfter(self.EndModal, wx.ID_CANCEL)

        except scanner.ScannerConnectionTimeoutError:
            self.__scanner = None
            wx.CallAfter(self.EndModal, wx.ID_CANCEL)

    
    def on_scanner_disconnect(self, evnt):
        """
        Event handler to handle scanner disconnect events. Shows a warning and
        exits the program.
        """
        self.__scanner.stop()
        wx.CallAfter(wx.MessageBox, "Scanner disconnected! Check the connection to your computer.", "AvoScan", wx.ICON_ERROR)
        wx.CallAfter(self.Destroy)        
        
        
    def onCancel(self, evnt):
        """
        Event handler for handling cancel button clicks. Stops any pending
        initialisation tasks and exits the program.
        """
        self.cancelled = True
        self.__scanner.cancel_initialisation()




class ScannerZeroingFrame(wx.Frame):
    """
    Frame with controls for setting the scanner's zenith position. This is the step position
    of the motor that will be treated as 90 degrees when determining scan angles.
    """
    def __init__(self, scanner):
        self.scanner = scanner
        self.scanner.disengage()
        
        wx.Frame.__init__(self, None, wx.ID_ANY, "AvoScan - Set zenith position")
        
        self.scanner.set_detach_handler(self.on_scanner_disconnect)
        
        art = AvoscanArtProvider()
        wx.ArtProvider.Push(art)
        self.SetIcon(art.GetIcon("Avoscan"))
        
        top_panel = wx.Panel(self, wx.ID_ANY)
                
        self.vsizer = wx.BoxSizer(wx.VERTICAL)
        
        self.step_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)
        
        self.manual_controls = manual.ManualControlPanel(top_panel, self.scanner)
        
        self.vsizer.Add(self.manual_controls, 0, wx.EXPAND)
                 
        self.cancel_button = wx.Button(top_panel, wx.ID_ANY, "Cancel")
        self.set_button = wx.Button(top_panel, wx.ID_ANY, "Set")
        self.buttons_sizer.Add(self.cancel_button, 0, wx.ALIGN_RIGHT)
        self.buttons_sizer.Add(self.set_button, 0, wx.ALIGN_RIGHT)
        self.buttons_sizer.AddSpacer(5)
        
        self.vsizer.Add(self.buttons_sizer, 1, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT)
        self.vsizer.AddSpacer(10)
        
        #fix the tabbing order for the buttons
        self.cancel_button.MoveAfterInTabOrder(self.set_button)
               
        top_panel.SetSizer(self.vsizer)
        self.vsizer.Fit(self)
        top_panel.SetAutoLayout(1)
        self.CenterOnScreen()
              
        #register event handlers
        wx.EVT_CLOSE(self, self.on_close)
        wx.EVT_BUTTON(self, self.cancel_button.GetId(), self.on_close)
        wx.EVT_BUTTON(self, self.set_button.GetId(), self.on_set_zenith)
        wx.Yield()
        
        self.scanner.set_zenith_as_here()

        wx.Yield()
        self.Show()
    
       
    def on_close(self, event):
        """
        Event handler for close events. Shuts down the scanner, destroys the 
        frame and exits the program.
        """
        self.Show(False)
        wx.Yield()
        self.scanner.close()
        self.Destroy()
       
        
    def on_scanner_disconnect(self, evnt):
        """
        Event handler to handle scanner disconnect events. Shows a warning and
        exits the program.
        """
        self.scanner.stop()
        wx.CallAfter(wx.MessageBox, "Scanner disconnected! Check the connection to your computer.", "AvoScan", wx.ICON_ERROR)
        wx.CallAfter(self.Destroy)
    
       
    def on_set_zenith(self, event):
        """
        Event handler for handling 'Set zenith' button clicks. Sets the zenith (90 degree)
        position of the scanner to its current position, closes the scanner zeroing frame 
        and opens the main AvoScan frame.
        """
        self.scanner.set_zenith_as_here()
        self.scanner.disengage()
        MainFrame(self.scanner)
        self.Destroy()
        
 
